package com.apes.framework.plugin.workflow.lib;

import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.Model;
import com.apes.framework.plugin.workflow.WorkflowEngine;
import com.apes.framework.plugin.workflow.domain.ProcessInstanceExt;
import com.apes.framework.plugin.workflow.domain.ProcessInstanceExtService;
import com.apes.framework.plugin.workflow.lib.callback.FlowExecuteInterface;
import com.apes.framework.plugin.workflow.parser.model.Node;
import com.apes.framework.plugin.workflow.parser.model.Workflow;
import com.apestech.api.workflow.WorkFlow;
import com.apestech.api.workflow.lib.element.WorkProcessEngine;
import com.apestech.framework.json.JSONObject;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

import static com.apes.framework.plugin.workflow.parser.model.Workflow.getWorkflow;


/**
 * 工作流相关API
 *
 * @author hup
 * @create 2018-07-23-17:00
 */
@Service
public class WorkflowApi extends DomainService implements WorkflowEngine {

    @Autowired
    private ProcessEngine engine;

    private static final String wf_content = "wf_content";
    private static final String wf_variable_prefix = "wf_variable_";

    @Override
    public String startProcessInstanceById(String processDefinitionKey, Map variables) {
        return null;
    }

    private FlowExecuteInterface flowInterface = new FlowExecuteInterface() {
        @Override
        public String getGateDataVar(String taskId) throws Exception {
            return getExclusiveGatewayVariableName(taskId);
        }

        @Override
        public List getGateData(String taskId) {
            return getExclusiveGateway(taskId);
        }

        @Override
        public void sendMsg(String topic, Object body) {
            sendAynEvent(topic, body);
        }

        @Override
        public <T> T invokeEsb(String method, Object body) {
            Object o = invoke(method, body);
            if(o instanceof Model && body instanceof Map){
                ((Map) body).put("version", ((Model) o).getVersion());
            }
            return (T) body;
        }
    };

    /**
     * 功能：执行任务
     *
     * @param taskId    任务ID
     * @param variables 输入参数
     */
    public void executeTask(String taskId, Map variables) {
        try {
            WorkPost workpost = new WorkPost(new WorkProcessEngine(engine), taskId, wf_content, wf_variable_prefix, flowInterface);
            workpost.executeTask(variables);
//            if(!taskId.equals(workpost.getTask().getId())) throw  new RuntimeException("任务执行错误"); // 串评论异常处理
            logger(taskId, workpost.getWebmap(), workpost.getOperGateData());
            sendCarbonCopyMsg(taskId, workpost.getWebmap(), workpost.getOperGateData());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public void sendExtendMsg(JSONObject input, Boolean innerChangeSignFlag) throws Exception {
        String taskId=input.getString("taskId");
        WorkPost workpost=new WorkPost(new WorkProcessEngine(engine), taskId, wf_content, wf_variable_prefix, flowInterface);
//        if(!taskId.equals(workpost.getTask().getId())) throw  new RuntimeException("任务执行错误"); // 串评论异常处理
        workpost.sendExtendMsg(input, false);
        logger(taskId, input, null);
        if( !innerChangeSignFlag ) sendCarbonCopyMsg(taskId, input, null);
    }

    public void sendExtendMsg(JSONObject input) throws Exception {
        sendExtendMsg(input, false);
    }

    public void sendStopMsg(Task task, JSONObject input) throws Exception {
        String taskId=input.getString("taskId");
        WorkPost workpost=new WorkPost(new WorkProcessEngine(engine), task, wf_content, wf_variable_prefix, flowInterface);
//        if(!taskId.equals(workpost.getTask().getId())) throw  new RuntimeException("任务执行错误"); // 串评论异常处理
        workpost.sendExtendMsg(input, true);
        logger(taskId, input, null);
        sendCarbonCopyMsg(taskId, input, null);
    }

    /**
     * 发送抄送消息
     * @param
     * @throws Exception
     */
    public void sendCarbonCopyMsg(String taskId, Map variables, List<Map> rows) throws Exception{
        if(variables.get("wf_carbonCopy") == null) return;
        TaskInfo task = findTaskInfoById(taskId);
        if(task == null) return;

        // 获取流程额外信息(BusinessType, BusinessKey)
        ProcessInstanceExt processInstanceExt = SpringManager.getBean(ProcessInstanceExtService.class).findByProcessInstanceId(task.getProcessInstanceId());
        if(processInstanceExt == null) return;

        // 获取流程对应的信息（formId, api, processDefinitionName）
        Object pi = OAUtil.findProcessInstance(engine, task.getProcessInstanceId());

        String processDefinitionKey = "";
        String processDefinitionName = "";
        if (pi instanceof HistoricProcessInstance) {
            processDefinitionKey = ((HistoricProcessInstance) pi).getProcessDefinitionKey();
            processDefinitionName = ((HistoricProcessInstance) pi).getProcessDefinitionName();
        } else if (pi instanceof ProcessInstance) {
            processDefinitionKey = ((ProcessInstance) pi).getProcessDefinitionKey();
            processDefinitionName = ((ProcessInstance) pi).getProcessDefinitionName();
        }

        Workflow wf = getWorkflow(processDefinitionKey);
        Node node = wf.getTaskNode(task.getTaskDefinitionKey());

        String variableName=getExclusiveGatewayVariable(rows, variables);
        String operate = task.getName() + (variableName == null ? "" : "：" + variableName);
        sendSynEvent("event:wf.carbonCopy", mapper(
                "sourceBusinessType", processInstanceExt.getBusinessType(),
                "sourceBusinessKey",processInstanceExt.getBusinessKey(),
                "operate", operate,
                "content", variables.get("wf_comments"),
                "from_id", variables.get("taskExecutor"),
                "to_id", variables.get("wf_carbonCopy"),
                "processDefinitionName", processDefinitionName,
                "formId", node.getForm().getId(),
                "api", node.getForm().getApi(),
                "checked", false
        ));
    }

    @Override
    public List<Map> getTasks(String userId) {
        return null;
    }

    @Override
    public List<Map> getTasks(String userId, int page, int size) {
        return null;
    }

    @Override
    public Map getTaskByTaskId(String taskId) {
        return null;
    }

    @Override
    public Long getTaskCount(String userId) {
        return null;
    }

    /**
     * 功能：取下一步任务的排它网关信息
     *
     * @param taskId
     * @return
     */
    public List getExclusiveGateway(String taskId) {
        try {
            WorkFlow workFlow = new WorkFlow(engine);
            return workFlow.getDecision(taskId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据一个流程实例id挂起该流程实例
     *
     * @param processInstanceId
     */
    @Override
    public void suspendProcessInstanceById(String processInstanceId) {

    }

    /**
     * 根据一个流程实例id激活该流程实例
     *
     * @param processInstanceId
     */
    @Override
    public void activateProcessInstanceById(String processInstanceId) {

    }

    private String getExclusiveGatewayVariableName(String taskId) {
        List<Map> rows = getExclusiveGateway(taskId);
        if (rows == null) return null;
        return (String) rows.stream().findFirst().get().get("valueId");
    }

    private void logger(String taskId, Map variables, List<Map> rows) {
        TaskInfo task = findTaskInfoById(taskId);
        if(task == null) return;
        ProcessInstanceExt processInstanceExt = SpringManager.getBean(ProcessInstanceExtService.class).findByProcessInstanceId(task.getProcessInstanceId());
        if(processInstanceExt == null) return;

        String variableName=getExclusiveGatewayVariable(rows, variables);
        String operate = task.getName() + (variableName == null ? "" : "：" + variableName);
        sendSynEvent("event:ir.logger.create", mapper(
                "businessType", processInstanceExt.getBusinessType(),
                "businessKey", processInstanceExt.getBusinessKey(),
                "operate", operate,
                "workPostName",null,
                "remark", variables.get("wf_comments"),
                "oaFlag", true,
                "operator_id", variables.get("taskExecutor")));
    }

    private TaskInfo findTaskInfoById(String taskId) {
        TaskInfo taskInfo = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
        if (taskInfo == null) {
            taskInfo = engine.getHistoryService().createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        }
        return taskInfo;
    }

    private String getExclusiveGatewayVariable(List<Map> rows, Map variables) {
        if (rows == null) return null;
        String variableName = (String) rows.stream().findFirst().get().get("valueId");
        String field = wf_variable_prefix + variableName;
        if (!variables.containsKey(field) && !variables.containsKey(variableName)) return null;
        Object value = variables.get(field) != null ? variables.get(field) : variables.get(variableName);
        try {
            return (String) rows.stream().filter(row ->
                    row.get("value").equals(value)
            ).findFirst().get().get("name");
        } catch (Exception e) {
            return null;
        }
    }

}
